package com.hzw.saas.service.news.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.http.HttpDownloader;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.news.INewsService;
import com.hzw.saas.api.news.bo.NewsBO;
import com.hzw.saas.api.news.bo.NewsContentBO;
import com.hzw.saas.api.property.IPropertyService;
import com.hzw.saas.api.property.enmus.SysParamKeyEnum;
import com.hzw.saas.api.storage.ICatalogService;
import com.hzw.saas.common.config.util.PageUtils;
import com.hzw.saas.common.storage.config.AwsS3Config;
import com.hzw.saas.common.storage.service.IS3FileService;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.common.util.StringUtils;
import com.hzw.saas.service.news.mapper.NewsMapper;
import com.hzw.saas.service.news.model.News;
import com.hzw.saas.service.storage.config.StorageConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 新闻接口默认实现
 *
 * @author zzl
 * @since 07/05/2021
 */
@Service("newsService")
@RequiredArgsConstructor
@Slf4j
public class NewsServiceImpl extends ServiceImpl<NewsMapper, News> implements INewsService, IService<News> {

    public static final String IMAGE_STR_SPLIT_CHAR = ";";

    @Resource(name = IPropertyService.SYS_PARAM_BEAN)
    private IPropertyService<SysParamKeyEnum> propertyService;

    private final AwsS3Config s3Config;
    private final IS3FileService s3FileService;
    private final ICatalogService catalogService;
    private final StorageConfig storageConfig;

    @Override
    public IPage<NewsBO> pageNews(IPage<NewsBO> page) {
        IPage<News> pageDO = PageUtils.convert(page, News.class);
        IPage<News> pageResultDO = super.lambdaQuery()
            .select(News.class, info -> !"content".equals(info.getColumn()))
            .orderByDesc(News::getCreateTime)
            .page(pageDO);

        List<NewsBO> result = new ArrayList<>();
        for (News record : pageResultDO.getRecords()) {
            NewsBO bo = new NewsBO();
            this.convertNewsToBo(record, bo);
            result.add(bo);
        }
        IPage<NewsBO> pageResult = new Page<>(pageResultDO.getCurrent(), pageResultDO.getSize(), pageResultDO.getTotal());
        pageResult.setRecords(result);
        return pageResult;
    }

    @Override
    public IPage<NewsBO> pageNews2Admin(IPage<NewsBO> page) {
        return this.baseMapper.pageNews2Admin(page);
    }

    @Override
    public NewsContentBO getNewsContent(String pid) {
        if (StrUtil.isBlank(pid))
            return null;
        News newsDO = super.getById(pid);
        if (Objects.isNull(newsDO))
            return null;
        NewsContentBO newsContentBO = new NewsContentBO();
        this.convertNewsToBo(newsDO, newsContentBO);
        return newsContentBO;
    }

    private void convertNewsToBo(News src, NewsBO tar) {
        if (Objects.isNull(src))
            return;
        MapperUtil.nf().map(src, tar);
        if (StrUtil.isNotBlank(src.getTitleImage())) {
            List<String> images = StrUtil.split(src.getTitleImage(), IMAGE_STR_SPLIT_CHAR);
            images = StringUtils.trimAndDistinct(images);
            tar.setTitleImages(images);
        }
    }

    @Override
    public Boolean modifyNews(NewsContentBO newsContentBO) {
        News news = new News();
        MapperUtil.nf().map(newsContentBO, news);
        // 220106 处理从微信公众号文章复制内容中的视频
        news.setContent(this.dealVideoContent(news.getContent()));

        boolean result = false;
        if (StrUtil.isBlank(news.getPid())) {
            news.setPid(SnowFlakeUtil.nextIdStr());
            result = this.save(news);
        } else {
            news.setCreatorId(null);
            news.setUpdateTime(new Date());
            result = this.updateById(news);
        }

        // 220107 提取新闻中的视频，上传到s3并替换原src地址
        ThreadUtil.execute(() -> {
            this.extractVideo2S3(news);
        });

        return result;
    }

    @Override
    public Boolean deleteNews(String pid) {
        if (StrUtil.isBlank(pid))
            return false;
        return this.removeById(pid);
    }

    /**
     * 处理从微信公众号文章复制内容中的视频
     * 220106
     *
     * @param htmlContent
     * @return
     */
    private String dealVideoContent(String htmlContent) {
        // 处理转义字符
        HtmlUtil.unescape(htmlContent);
        htmlContent = htmlContent.replaceAll("\\\\\"", "\"");
        Document doc = Jsoup.parse(htmlContent);
        // 删除 class=video_mask 的section标签
        doc.select("section[class=video_mask]").remove();
        // video的标签上添加一个 controls="controls"
        doc.select("video").attr("controls", "controls");
        return doc.html();
    }

    /**
     * 提取新闻中的视频，上传到s3并替换原src地址
     * <p>
     * 220107
     *
     * @param news
     */
    private void extractVideo2S3(News news) {
        Document doc = Jsoup.parse(news.getContent());
        Elements elements = doc.select("video");
        for (Element element : elements) {
            String srcAttrKey = "src";
            String src = element.attr(srcAttrKey);
            if (StrUtil.isBlank(src)) {
                continue;
            }
            if (src.contains("amazonaws.com.cn")) {
                continue;
            }
            try {
                String dirPath = catalogService.getCatalogPath(storageConfig.getPersonalSpaceCacheCatalog());
                File dir = FileUtil.mkdir(dirPath);
                // 下载视频文件
                File file = HttpDownloader.downloadForFile(src, dir, -1, null);
                if (!file.exists() || file.length() == 0) {
                    throw new RuntimeException("文件下载失败");
                }
                String s3Key = news.getPid() + "." + FileNameUtil.getSuffix(file);
                // 上传视频文件
                String bucket2News = propertyService.getPropertyValue("aws.s3", "bucket2News", "");
                if (StrUtil.isBlank(bucket2News)) {
                    throw new RuntimeException("未设置公共权限的存储桶");
                }
                s3FileService.uploadFile(file, s3Key, bucket2News);
                if (!s3FileService.exists(s3Key, bucket2News)) {
                    throw new RuntimeException("s3文件上传失败");
                }
                // 替换视频地址
                element.attr(srcAttrKey, String.format(s3Config.getS3UrlFormat(), bucket2News, s3Config.getRegion(), s3Key));
            } catch (Exception e) {
                log.error(String.format("转存新闻(%s)视频文件(%s)异常", news.getPid(), src), e);
            }
        }
        String newContent = doc.html();
        if (!Objects.equals(news.getContent(), doc.html())) {
            super.lambdaUpdate().eq(News::getPid, news.getPid()).set(News::getContent, newContent).update();
        }
    }

}
